//
//
//
//
//
// Parser.C
// 2011/08/29 @CieNET

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <list>

#include "Parser.h"
#include "LogManager.h"
#include "StrUtil.h"

namespace TinyXML
{
	Parser::Parser()
		: _root(NULL), _current(NULL), _currentState(Parser::NODE_INITIAL), _line(0), _mapAddr(NULL)
	{

	}
	Parser::Parser(const std::string& fileName)
	{
		InitialParser(fileName);
	}
	Parser::~Parser(){};

	void Parser::Parse(const std::string& fileName)
	{
		InitialParser(fileName);
		Parse();
	}

	void Parser::Parse()
	{
		std::string o_name;
		Properties o_property;
		Parser::PARSE_STATE r_state;

		try
		{
			r_state = PraseSymbol(_currentState, o_name, o_property);
			if(Parser::NODE_END == r_state
					&& o_name == _root->GetName())
			{
				LOGINFO("Final Node found, would return");
			}
		}
		catch(const Exception& exp)
		{
			Util::StrStream str;
			str << "ERROR parse : " << _file.Path()
				<< "MSG: " << exp.What();
			throw Exception(str);
		}
		catch(...)
		{
			LOGERROR("Unknow exception caught");
			throw;
		}
	}

	Parser::PARSE_STATE Parser::PraseSymbol(PARSE_STATE current, std::string& name, Properties& property)
	{
		// the current file read ptr should be ready

		char* start =(char*) _mapAddr;
		struct stat _stat;
		_file.Stat(_stat);

		char* end = start + _stat.st_size;

		std::list<Node*> uncomNodes;

		std::string symbol;

		Node* pre = NULL;

		while(MoveToNextSym((char*) _mapAddr, start, end, symbol))
		{
			LOGINFO(symbol);
			if(symbol.empty())
				LOGWARN("empty symbol found");

			bool bNaked = false;
			ReadWord((char*) _mapAddr, start, end, symbol, name, bNaked);

			if(bNaked)
			{
				std::string str = name;
				if(!Util::Trim(name).empty())
				{
					Node* child = new Node("", "", name, NULL);
					if(uncomNodes.empty())
						throw Exception("naked node found when empty vector");
					pre = uncomNodes.back();
					pre->AddChild(child);
					child->AddParent(pre);
				}
				continue;
			}


			// Start symbol
			if(symbol == "<" )
			{
				pre = NULL;
				if(!uncomNodes.empty())
					pre = uncomNodes.back();

				Node* child = new Node("", name, "", NULL);
				uncomNodes.push_back(child);
				// the first constructure
				if(_root == NULL)
				{
					_root =  child;
					continue;
				}

				if(pre)
				{
					child->AddParent(pre);
					pre->AddChild(child);
				}
				continue;
			}

			// Node End symbol
			if(symbol == "</")
			{
				if(uncomNodes.empty())
					throw Exception("empty stack, but '</' found");

				Node* node = uncomNodes.back();
				node->SetEnd(name);
				uncomNodes.pop_back();
				continue;
			}

			LOGERROR(name);
		}

		if(!uncomNodes.empty())
			throw Exception("uncomNodes not empty");
		return Parser::NODE_START;
	}


	// check whether till end all blank (not human readable charactors)
	// if start == end, then return true
	bool Parser::IsAllBlank(char* start, const char* end)
	{
		if(NULL == start || NULL == end)
			return true;
		for(; start <= end; ++start)
		{
			if (int(*start) >= 0x21 && int(*start) <= 0x7e)
				return false;
		}
		return true;
	}

	// usually, it would found symbol from the buf, if not that means then end or an error
	// throw the error or return false to indicate then end
	bool Parser::MoveToNextSym(const char* start, char*& ptr, const char* end, std::string& symbol /* OUT */)
	{
		symbol = "";
		for(; ptr <= end; ++ptr)
		{
			if(*ptr!= '<' && *ptr != '>')
				continue;

			if(*ptr == '<' && IsAllBlank(ptr, end))
				throw Exception("Error end, when found '<'");

			// End symbol found
			if(*ptr== '>')
			{
				symbol = '>';
				if(ptr > start && *(ptr - 1) == '?')
					symbol = "?>";
				if(ptr+ 1 < end)
					ptr++;
				return IsAllBlank(ptr+ 1, end) ? false : true;
			}

			// Start symbol found && not all blank left
			else
			{
				++ptr;
				if(*ptr== '/')
				{
					++ptr;
					symbol = "</";
					return IsAllBlank(ptr+ 1, end) ? false : true;
				}
				if(*ptr== '?')
				{
					++ptr;
					symbol = "<?";
					return IsAllBlank(ptr+ 1, end) ? false : true;
				}
				symbol = "<";
				return true;
			}
		}
		// No symbol found, usually it indicate an error.
		return false;
	}

	//
	//
	void Parser::ReadWord(const char* start, char*& ptr, const char* end, const std::string& symbol, std::string& name, bool& naked)
	{
		naked = false;

		char* ptrOrigin = ptr;
		std::string nextSymbol;
		bool bEnd = !MoveToNextSym(start, ptrOrigin, end, nextSymbol);
		if(bEnd && nextSymbol.empty())
		{
			LOGINFO("Error read word, file end & next symbol empty");
			throw Exception("Error read word, file end & next symbol emptry");
		}

		// <? **** ?>
		// < ** >
		// > ** <
		// > ** </
		if(symbol == "<?" && nextSymbol != "?>")
			throw Exception("Error match <? *** ?>");
		if(symbol == "<" && nextSymbol != ">")
			throw Exception("Error match < *** >");
		if(symbol == ">" && nextSymbol != "<" && nextSymbol != "</")
			throw Exception("Error match > *** < or > *** </");

		char* szBuf = new char[ptrOrigin - ptr + 1];
		memset(szBuf, 0, ptrOrigin - ptr + 1);
		memcpy(szBuf, ptr, ptrOrigin - ptr - nextSymbol.length());
		name = szBuf;
		delete szBuf;

		if(symbol == ">" && (nextSymbol == "<"
					|| nextSymbol == "</"))
			naked = true;
	}


	void Parser::InitialParser(const std::string& path)
	{
		_file.Close();
		_root = _current = NULL;
		_line = 0;
		_mapAddr = NULL;

		_file.Open(path, File::_O_RDWR, 0);

		struct stat _stat;
		_file.Stat(_stat);
		_file.MapToMem(_mapAddr, _stat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, 0);
	}

	//
	//
	// Get Node Content
	// name : [VAR1]:[VAR2]:[VAR3]
	bool Parser::GetNodeContent(const std::string& name, std::string& content)
	{
		std::vector<std::string> vec;

		char szBuf[64];
		U32 index = 0;
		U32 pos = -1;
		U32 lPos = -1;
		for(std::string::const_iterator it = name.begin(); ;++it, ++index)
		{
			if(*it == ':' || it == name.end())
			{
				memset(szBuf, 0, sizeof(szBuf));
				lPos = pos;
				pos = index; 
				name.copy(szBuf, pos - lPos - 1, lPos + 1);
				vec.push_back(std::string(szBuf));
			}

			if(it == name.end())
				break;
		}
		
		if(vec.size() == 0)
			return false;
		
		Node* it_node = _root;
		for(std::vector<std::string>::iterator vec_it = vec.begin();
				vec_it != vec.end();
				++vec_it)
		{
			if(vec_it == vec.begin())
			{
				// first find
				if(it_node->GetName() != (*vec_it))
					throw Exception("Not found child, at root position");

				continue;
			}
			it_node = FindFirstChildNode(it_node, (*vec_it));
			if(it_node == NULL)
			{
				throw Exception(std::string("Not found child") + (*vec_it));
			}
		}
		return true;
	}

	// FindFirstChildNode
	Node* Parser::FindFirstChildNode(Node* node, const std::string& name)
	{
		if(NULL == node)
			throw Exception("NULL parameter when FindFirstChildNode is called");
		Node* childs = node->GetChild();
		if(NULL == childs)
			return NULL;

		do
		{
			if(childs->GetName() == name)
				return childs;

		}while(childs = childs->GetNextSibling());
		
		return NULL;
	}

	// Get root node
	Node* Parser::GetRoot(void)
	{
		return _root;
	}

	void Parser::WriteToFile(const std::string& path)
	{
		try
		{
			U32 indent = -4;
			File file(path);
			file.Open(File::_O_WRONLY | File::_O_CREAT, File::_S_IWUSR |
					File::_S_IRUSR | File::_S_IRGRP | File::_S_IWGRP | File::_S_IROTH);
			WriteNode(file, indent, _root);
			file.Close();
		}
		catch(const Exception& exp)
		{
			LOGERROR(exp.What());
			return;
		}
		catch(...)
		{
			LOGERROR("Unknow error");
			throw;
		}
	}

	// dump current xml tree back to file
	void Parser::WriteToFile(void)
	{
	}

	// Write the indicated node to file opened
	//
	//
	void Parser::WriteNode(File& file, U32& indent, Node* node)
	{
		if(node == NULL)
			return;
		if(!node->GetName().empty())
		{
			if(node->GetChild() && node->GetChild()->GetName().empty())
			{
				indent += 4;
				file << File::GetSpaceN(indent) 
					<< node->GetNodeStart()
					<< node->GetChild()->GetContext()
					<< node->GetNodeEnd()
					<< File::ENDL;
				indent -= 4;
			}
			else
			{

				indent += 4;
				file << File::GetSpaceN(indent) << node->GetNodeStart() << File::ENDL;
				WriteNode(file, indent, node->GetChild());
				file << File::GetSpaceN(indent) << node->GetNodeEnd() << File::ENDL;
				indent -= 4;
			}
		}

		WriteNode(file, indent, node->GetNextSibling());
	}

	// debug purpose
	void Parser::DumpNode(Node* node)
	{
		if(NULL == node)
			return;

		Util::StrStream str;
		str << "Name:" << node->GetName()
			<< " Context:" << node->GetContext();
		std::cout << str.Str() << std::endl;

		if(node->GetChild())
			DumpNode(node->GetChild());
		if(node->GetNextSibling())
			DumpNode(node->GetNextSibling());
	}
	// dump all, debug purpose
	void Parser::DumpAll(void)
	{
		DumpNode(_root);
	}
};
